<?php

namespace Mogod\Core\Data;

require_once __DIR__ . '/DataRepositoryAbstract.php';

/**
 *
 * Транспорт данных
 *
 * @author goorus, morph
 *
 */
class DataTransport extends DataRepositoryAbstract
{

	/**
	 * Поставщики данных.
	 *
	 * @var array of DataRepositoryAbstract
	 */
	protected $providers = array();

	/**
	 * Стек начатых транзакций
	 *
	 * @var array
	 */
	protected $transactions = array();

	/**
	 * Добавляет провайдера последним в конец списка
	 *
	 * @param DataRepositoryAbstract $provider
	 * @return $this
	 */
	public function appendProvider()
	{
		$this->providers = array_merge($this->providers, func_get_args());
		return $this;
	}

	/**
	 * Начинает новую транзакцию
	 *
	 * @return $this
	 */
	public function beginTransaction()
	{
		$transaction = new DataTransaction($this);
		$this->transactions[] = $transaction;
		return $this;
	}

	/**
	 * Заканчивает текущую транзакцию
	 * @return DataTransaction Законченная транзакция
	 */
	public function endTransaction()
	{
		return array_pop($this->transactions);
	}

	/**
	 * Очистка данных всех провайдеров или текущей транзации.
	 *
	 * @return $this
	 */
	public function flush()
	{
		if ($this->transactions) {
			end($this->transactions)->flush();
		} else {
			foreach ($this->providers as $provider) {
				$provider->flush();
			}
		}
		return $this;
	}

	/**
	 * Получение данных. Получает значение для переданного ключа (ключей).
	 * Для каждого ключа будет возвращено значение из провайдера, который
	 * вернет отличное от null.
	 *
	 * @param mixed $_ Ключ или список ключей
	 * @return mixed Значение или список значений
	 */
	public function get($key)
	{
		$keys = func_get_args();
		$results = array();

		if ($this->transactions) {
			$buffer = end($this->transactions)->getAll();
			foreach ($keys as $key) {
				$results[] = isset($buffer[$key]) ? $buffer[$key] : null;
			}
		} else {
			foreach ($keys as $key) {
				$data = null;
				foreach ($this->providers as $provider) {
					$chunk = $provider->get($key);
					if ($chunk !== null) {
						$data = $chunk;
						break;
					}
				}
				$results[] = $data;
			}
		}

		return count($results) == 1 ? $results[0] : $results;
	}

	/**
	 * Возвращает все значения из всех провайдеров.
	 * Если начата транзакция, будет возвращаен буффер транзакции.
	 *
	 * @return array Массив пар (ключ => значение)
	 */
	public function getAll()
	{
		if ($this->transactions) {
			return end($this->transactions)->getAll();
		}

		$result = array();
		foreach ($this->providers as $provider) {
			$result = array_merge($result, $provider->getAll());
		}

		return $result;
	}

	/**
	 * Возвращает текущую транзакцию
	 *
	 * @return DataTransaction Текущая транзакция или false
	 */
	public function getCurrentTransaction()
	{
		return end($this->transactions);
	}

	/**
	 * Возвращает провайдера по индексу
	 *
	 * @param integer $index Индекс
	 * @return DataRepositoryAbstract Провайдер
	 */
	public function getProvider($index)
	{
		return $this->providers[$index];
	}

	/**
	 * Возвращает всех провайдеров
	 *
	 * @return array of DataRepositoryAbstract
	 */
	public function getProviders()
	{
		return $this->providers;
	}

	/**
	 * Устанавливает несколько значений
	 *
	 * @param array $data
	 * @return $this
	 */
	public function mset(array $data)
	{
		if ($this->transactions) {
			end($this->transactions)->mset($data);
		} else {
			foreach ($this->providers as $provider) {
				$provider->mset($data);
			}
		}

		return $this;
	}

	/**
	 * Добавляет провайдера последним в начало списка
	 *
	 * @param DataRepositoryAbstract $provider
	 * @return $this
	 */
	public function prependProvider($provider)
	{
		array_unshift($this->providers, $provider);
		return $this;
	}

	/**
	 * Очищает список провайдеров
	 *
	 * @return $this
	 */
	public function removeProviders()
	{
		$this->providers = array();
		return $this;
	}

	/**
	 * Устанавливает значение в провайдерах.
	 * Если есть активная транзакция, значение попадет в буффер.
	 *
	 * @param string $key
	 * @param mixed $data
	 * @return $this
	 */
	public function set($key, $data)
	{
		if ($this->transactions) {
			end($this->transactions)->set($key, $data);
		} else {
			foreach ($this->providers as $provider) {
				$provider->set($key, $data);
			}
		}
		return $this;
	}

}